//
//  SLTransitionContainerController.m
//  Objective-C
//
//  Created by S_Line on 2018/7/6.
//  Copyright © 2018年 S_Line. All rights reserved.
//

#import "SLTransitionContainerController.h"

#import "Animator.h"

static CGFloat const kButtonWidth = 44;
static CGFloat const kButtonHeight = 44;

/**
 Instances of this private class perform the default transition animation which is to slide child views horizontally.
 
 @note The class only supports UIViewControllerAnimatedTransitioning at this point. Not UIViewControllerInteractiveTransitioning.
 */
@interface PrivateAnimatedTransition: NSObject<UIViewControllerAnimatedTransitioning>

@end

@interface SLTransitionContainerController ()

@property (nonatomic, strong) NSArray *viewControllers;
@property (nonatomic, strong) UIView *buttonsView;   /// The view hosting the buttons of the child view controllers.
@property (nonatomic, weak) UIView *containerView;   /// The view hosting the child view controllers views.

@property (nonatomic, weak) UIViewController *selectedViewController;   /// The currently selected and visible child view controller.

@end

@implementation SLTransitionContainerController

- (void)setSelectedViewController:(UIViewController *)selectedViewController {
    if (!selectedViewController || ![selectedViewController isKindOfClass:[UIViewController class]]) {
        return;
    }
    
    [self transitionToChildViewController:selectedViewController];
    _selectedViewController = selectedViewController;
    [self updateButtonSelection];
}

- (instancetype)initWithViewControllers:(NSArray *)viewControllers {
    self = [super init];
    
    if (self) {
        self.viewControllers = [viewControllers copy];
    }
    
    return self;
}

- (void)initNavBar {
    self.navTitle = @"转场动画";
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initNavBar];
    
    [self initView];
    
    [self addChindViewControllerButtons];
    
    self.selectedViewController = self.viewControllers[0];
}

- (void)initView {
    UIView *containerView = [[UIView alloc] init];
    self.containerView = containerView;
    containerView.backgroundColor = [UIColor blackColor];
    containerView.opaque = true;
    [self.view addSubview:containerView];
    [containerView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.bottom.mas_equalTo(0);
    }];
    
    UIView *buttonsView = [[UIView alloc] init];
    self.buttonsView = buttonsView;
    buttonsView.backgroundColor = [UIColor clearColor];
    buttonsView.tintColor = [UIColor colorWithWhite:1.0 alpha:0.6];
    [self.view addSubview:buttonsView];
    [buttonsView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.width.mas_equalTo(kButtonWidth * self.viewControllers.count);
        make.height.mas_equalTo(kButtonHeight);
        make.centerX.equalTo(self.view.mas_centerX);
        make.top.mas_equalTo(100);
    }];
}

- (void)addChindViewControllerButtons {
    [self.viewControllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL * _Nonnull stop) {
        UIButton *button = [[UIButton alloc] init];
        UIImage *image = [vc.tabBarItem.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        [button setImage:image forState:UIControlStateNormal];
        UIImage *selectedImage = [vc.tabBarItem.selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        [button setImage:selectedImage forState:UIControlStateSelected];
        button.tag = idx;
        [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
        [self.buttonsView addSubview:button];
        [button mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.mas_equalTo(kButtonWidth * idx);
            make.top.mas_equalTo(0);
            make.width.mas_equalTo(kButtonWidth);
            make.height.mas_equalTo(kButtonHeight);
        }];
    }];
}

- (void)transitionToChildViewController:(UIViewController *)toVC {
    UIViewController *fromVC = self.selectedViewController ?: nil;
    if (toVC == fromVC || ![self isViewLoaded]) {
        return;
    }
    
    UIView *toView = toVC.view;
    toView.frame = self.containerView.bounds;
    
    [self addChildViewController:toVC];
    
    // If this is the initial presentation, add the new child with no animation.
    if (!fromVC) {
        [self.containerView addSubview:toVC.view];
        return;
    }
    
    id<UIViewControllerAnimatedTransitioning> animator = nil;
    if ([self.delegate respondsToSelector:@selector(containerViewController:animationControllerForTransitionFromViewController:toViewController:)]) {
        animator = [self.delegate containerViewController:self animationControllerForTransitionFromViewController:fromVC toViewController:toVC];
    }
    animator = animator ?: [[PrivateAnimatedTransition alloc] init];
    // Animate the transition by calling the animator with our private transition context.
//    Animator *animator = [[Animator alloc] init];
    
    // Because of the nature of our view controller, with horizontally arranged buttons,
    // we instantiate our private transition context with information about whether this is a left-to-right or right-to-left transition.
    // The animator can use this information if it wants.
    NSUInteger fromIndex = [self.viewControllers indexOfObject:fromVC];
    NSUInteger toIndex = [self.viewControllers indexOfObject:toVC];
    PrivateTransitionContext *transitionContext = [[PrivateTransitionContext alloc] initWithFromViewController:fromVC toViewController:toVC goingRight:toIndex > fromIndex];
    transitionContext.animated = true;
    transitionContext.interactive = false;
    transitionContext.completionBlock = ^(BOOL didComplete) {
        [fromVC removeFromParentViewController];
        
        if ([animator respondsToSelector:@selector(animationEnded:)]) {
            [animator animationEnded:didComplete];
        }
    };
    [animator animateTransition:transitionContext];
}

- (void)updateButtonSelection {
    [self.buttonsView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[UIButton class]]) {
            ((UIButton *)obj).selected = self.viewControllers[idx] == self.selectedViewController;
        }
    }];
}

#pragma mark ----- 点击事件
- (void)buttonClick:(UIButton *)button {
    UIViewController *vc = self.viewControllers[button.tag];
    self.selectedViewController = vc;
    
    if ([self.delegate respondsToSelector:@selector(containerViewController:didSelectViewController:)]) {
        [self.delegate containerViewController:self didSelectViewController:vc];
    }
}

@end

#pragma mark ----- Private Transitioning Classes
@interface PrivateTransitionContext()

@property (nonatomic, strong) NSDictionary *viewControllers;
@property (nonatomic, assign) CGRect disappearingFromRect;
@property (nonatomic, assign) CGRect appearingFromRect;
@property (nonatomic, assign) CGRect disappearingToRect;
@property (nonatomic, assign) CGRect appearingToRect;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle;


@end

@implementation PrivateTransitionContext

- (instancetype)initWithFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC goingRight:(BOOL)goingRight {
    self = [super init];
    
    if (self) {
        NSAssert([fromVC isViewLoaded] && fromVC.view.superview, @"The fromViewController's view must reside in the container view upon initializing the transition context.");
        
        self.presentationStyle = UIModalPresentationCustom;
        self.containerView = fromVC.view.superview;
        self.viewControllers = @{
                                 UITransitionContextFromViewControllerKey: fromVC,
                                 UITransitionContextToViewControllerKey: toVC
                                 };
        
        // Set the view frame properties which make sense in our specialized ContainerViewController context.
        // Views appear from and disappear to the sides, corresponding to where the icon buttons are positioned.
        // So tapping a button to the right of the currently selected, makes the view disappear to the left and the new view appear from the right.
        // The animator object can choose to use this to determine whether the transition should be going left to right, or right to left, for example.
        CGFloat travelDistance = goingRight ? -self.containerView.bounds.size.width : self.containerView.bounds.size.width;
        self.disappearingFromRect = self.appearingToRect = self.containerView.bounds;
        self.disappearingToRect = CGRectOffset(self.containerView.bounds, travelDistance / 2.0, 0);
        self.appearingFromRect = CGRectOffset(self.containerView.bounds, -travelDistance / 2.0, 0);
        
    }
    
    return self;
}

- (CGRect)initialFrameForViewController:(UIViewController *)vc {
    if (vc == [self viewControllerForKey:UITransitionContextFromViewControllerKey]) {
        return self.disappearingToRect;
    }
    
    return self.appearingToRect;
}

- (CGRect)finalFrameForViewController:(UIViewController *)vc {
    if (vc == [self viewControllerForKey:UITransitionContextFromViewControllerKey]) {
        return self.disappearingToRect;
    }
    
    return self.appearingToRect;
}

- (nullable UIViewController *)viewControllerForKey:(NSString *)key {
    return self.viewControllers[key];
}

- (void)completeTransition:(BOOL)didComplete {
    if (self.completionBlock) {
        self.completionBlock(didComplete);
    }
}

// Our non-interactive transition can't be cancelled (it could be interrupted, though)
- (BOOL)transitionWasCancelled {
    return false;
}

- (void)updateInteractiveTransition:(CGFloat)percentComplete {
    
}

- (void)finishInteractiveTransition {
    
}

- (void)cancelInteractiveTransition {
    
}

- (nullable __kindof UIView *)viewForKey:(nonnull UITransitionContextViewKey)key {
    return nil;
}
@synthesize targetTransform;

@end

@implementation PrivateAnimatedTransition

static CGFloat const kChildViewPadding = 16;
static CGFloat const kDamping = 0.75;
static CGFloat const kInitialSpringVelocity = 0.5;

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.5;
}

//  Slide views horizontally, with a bit of space between, while fading out and in.
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    // When sliding the views horizontally in and out, figure out whether we are going left or right.
    BOOL goingRight = [transitionContext initialFrameForViewController:toVC].origin.x < [transitionContext initialFrameForViewController:fromVC].origin.x;
    CGFloat travelDistance = [transitionContext containerView].bounds.size.width + kChildViewPadding;
    CGAffineTransform travel = CGAffineTransformMakeTranslation(goingRight ? travelDistance : -travelDistance, 0);
    
    [[transitionContext containerView] addSubview:toVC.view];
    toVC.view.alpha = 0;
    toVC.view.transform = CGAffineTransformInvert(travel);

    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:kDamping initialSpringVelocity:kInitialSpringVelocity options:UIViewAnimationOptionLayoutSubviews animations:^{
        fromVC.view.transform = travel;
        fromVC.view.alpha = 0;
        toVC.view.transform = CGAffineTransformIdentity;
        toVC.view.alpha = 1;
    } completion:^(BOOL finished) {
        fromVC.view.transform = CGAffineTransformIdentity;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end
